package v3.object.space;

import galaxy.obj.Point;
import galaxy.util.PercCounter;
import hipparcos.generate.LoadContext;
import hipparcos.mat.Sheet;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import log.LogUtil;

import org.apache.log4j.Logger;

import text.TextUtil;
import v3.object.STcfg;

public class Universe
{

	private int universeSize;
	// private HashMap<String, ArrayList<iSpaceObj>> universe;
	private Sector[][] newUniverse;

	private HashMap<String, ArrayList<iSpaceObj>> starList;
	private HashMap<String, ArrayList<iSpaceObj>> baseList;
	private HashMap<String, ArrayList<iSpaceObj>> enemyList;

	private ArrayList<Point> starlistCoordinate = new ArrayList<Point>();
	private ArrayList<Point> baseListCoordinate = new ArrayList<Point>();
	private ArrayList<Point> enemyListCoordinate = new ArrayList<Point>();
	private STcfg cfg;
	protected Logger log = LogUtil.getLog(this.getClass());
	public double starDate = 2371;// voyager
	private String dataPath;
	private Sheet T;
	private Sheet R;
	private Sheet M;
	private Sheet L;
	private Sheet atmPressSheet;
	private int base;
	private int enemy;
	private int star;
	private double enemyAtStarsP = .7; // ammonut of enemy generated around
										// stars in percent
	private double starbaseAtStarsP = .9;// ammount of starbase generated around
											// the stars in percent

	public Universe(STcfg cfg)
	{
		this.universeSize = cfg.getUniverseSize();
		this.cfg = cfg;
		this.dataPath = cfg.getDataPath();
	}

	public Sheet getAtmPressSheet()
	{
		return atmPressSheet;
	}

	public void setAtmPressSheet(Sheet atmPressSheet)
	{
		this.atmPressSheet = atmPressSheet;
	}

	public void generate() throws Exception
	{
		log.info("Generating Universe...");
		// universe = new HashMap<String, ArrayList<iSpaceObj>>();
		starList = new HashMap<String, ArrayList<iSpaceObj>>();
		baseList = new HashMap<String, ArrayList<iSpaceObj>>();
		enemyList = new HashMap<String, ArrayList<iSpaceObj>>();
		starlistCoordinate = new ArrayList<Point>();
		baseListCoordinate = new ArrayList<Point>();
		enemyListCoordinate = new ArrayList<Point>();
		newUniverse = new Sector[universeSize][universeSize];

		LoadContext lc = new LoadContext(cfg.getDataPath());
		lc.process();
		T = lc.getT();
		R = lc.getR();
		L = lc.getL();
		M = lc.getM();
		atmPressSheet = lc.getAtmPressSheet();

		base = cfg.getBaseAmmount();
		enemy = cfg.getEnemyAmmount();
		star = cfg.getStarAmmount();

		System.out.println("start...");
		if (cfg.isGenerateRandomAmmount())
		{
			base = (int) (cfg.getBaseAmmount() + cfg.rnd.nextGaussian() * 8);
			enemy = (int) (cfg.getEnemyAmmount() + cfg.rnd.nextGaussian() * 16);
			star = (int) (cfg.getStarAmmount() + cfg.rnd.nextGaussian() * 32);

			base = (base < 0 ? 2 : base);
			enemy = (enemy < 0 ? 4 : enemy);
			star = (star < 0 ? 8 : star);
		}
		log.info(TextUtil.append("generate: ", base, " base - ", enemy, " enemy ship - ", star, " stars..."));

		PercCounter pc = new PercCounter(base + enemy + star + universeSize * universeSize);

		for (int x = 0; x < universeSize; x++)
		{
			for (int y = 0; y < universeSize; y++)
			{
				Point p = new Point(x, y);
				newUniverse[x][y] = new Sector(p);
				if (pc.incAndShouldSaySomething())
					log.info("Universe Generate Sectors: " + pc.getSomething());
			}

		}

		// generate Stars
		for (int i = 0; i < star; i++)
		{
			if (pc.incAndShouldSaySomething())
				log.info("Universe Generate S: " + pc.getSomething());
			iSpaceObj o = new Star();
			o.generate(this);
			setObject(o);
		}

		// generate Enemies
		int enemyAtStars = (int) (enemy * enemyAtStarsP + .5);
		for (int i = 0; i < enemyAtStars; i++)
		{
			if (pc.incAndShouldSaySomething())
				log.info("Universe Generate E: " + pc.getSomething());
			iSpaceObj o = new Enemy();
			o.generate(this);
			int index = cfg.rnd.nextInt(starList.size());
			o.setPosition(starlistCoordinate.get(index));
			setObject(o);
		}

		for (int i = 0; i < enemy - enemyAtStars; i++)
		{
			if (pc.incAndShouldSaySomething())
				log.info("Universe Generate E: " + pc.getSomething());
			iSpaceObj o = new Enemy();
			o.generate(this);
			setObject(o);
		}

		// generate Bases
		int starbaseAtStars = (int) (base * starbaseAtStarsP + .5);

		for (int i = 0; i < starbaseAtStars; i++)
		{
			if (pc.incAndShouldSaySomething())
				log.info("Universe Generate B: " + pc.getSomething());
			iSpaceObj o = new StarBase();
			o.generate(this);
			int index = cfg.rnd.nextInt(starList.size());
			o.setPosition(starlistCoordinate.get(index));
			setObject(o);
		}

		for (int i = 0; i < base - starbaseAtStars; i++)
		{
			if (pc.incAndShouldSaySomething())
				log.info("Universe Generate B: " + pc.getSomething());
			iSpaceObj o = new StarBase();
			o.generate(this);
			setObject(o);
		}/**/

		System.out.println(toSquare());
	}

	public void setObject(iSpaceObj o) throws Exception
	{
		String pos = o.getPosition().toString();

		Sector s = newUniverse[o.getPosition().getiX()][o.getPosition().getiY()];

		if (s == null)
			s = new Sector(o.getPosition());

		Point sectorPosition = s.getFreeSectorPoint();
		o.setSectorPosition(sectorPosition);
		s.setISpaceObj(sectorPosition, o);
		newUniverse[o.getPosition().getiX()][o.getPosition().getiY()] = s;

		// in
		// ArrayList<iSpaceObj> list = universe.get(pos);
		// if (list == null)
		// {
		// list = new ArrayList<iSpaceObj>();
		// }
		// list.add(o);
		// universe.put(pos, list);

		if (o.getType().equals(SpaceObjType.Star))
		{
			starlistCoordinate.add(o.getPosition());
			ArrayList<iSpaceObj> listOBJ = starList.get(pos);
			if (listOBJ == null)
			{
				listOBJ = new ArrayList<iSpaceObj>();
			}
			listOBJ.add(o);
			starList.put(pos, listOBJ);
		}
		else if (o.getType().equals(SpaceObjType.StarBase))
		{

			baseListCoordinate.add(o.getPosition());
			ArrayList<iSpaceObj> listOBJ = baseList.get(pos);
			if (listOBJ == null)
			{
				listOBJ = new ArrayList<iSpaceObj>();
			}
			listOBJ.add(o);
			baseList.put(pos, listOBJ);
		}
		else if (o.getType().equals(SpaceObjType.Enemy))
		{
			enemyListCoordinate.add(o.getPosition());
			ArrayList<iSpaceObj> listOBJ = enemyList.get(pos);
			if (listOBJ == null)
			{
				listOBJ = new ArrayList<iSpaceObj>();
			}
			listOBJ.add(o);
			enemyList.put(pos, listOBJ);
		}
		else if (!o.getType().equals(SpaceObjType.Enterprise))
		{
			throw new Exception("Unknow Space Object type " + o);
		}
	}

	private String toSquare()
	{

		StringBuilder sb = new StringBuilder();
		int len = ("" + (universeSize - 1)).length();
		String mask = "00000000".substring(0, len);
		DecimalFormat f = new DecimalFormat(mask);
		for (int i = 0; i < len; i++)
		{
			sb.append(mask.replace("0", " ") + " ");
			for (int j = 0; j < universeSize; j++)
			{
				String s = f.format(j);
				s = s.substring(i, i + 1);
				sb.append(" ");
				sb.append(s);
				sb.append(" |");
			}
			sb.append("\n");
		}

		for (int y = 0; y < universeSize; y++)
		{
			sb.append(f.format(y) + " ");
			for (int x = 0; x < universeSize; x++)
			{
				Sector s = getSector(x, y);
				String info = " " + String.valueOf(Character.toChars(183)) + " ";
				if (!s.isEmpty())
				{
					info = s.getInfoString(SpaceObjType.Star, SpaceObjType.Enemy, SpaceObjType.StarBase);
				}
				sb.append(info.replace(";", ""));
				sb.append("|");
			}
			sb.append("\n");
		}

		return sb.toString();
	}

	public Point getEmptyPoint() throws Exception
	{
		Point p = null;
		int retry = universeSize * universeSize * universeSize * universeSize;
		while (p == null && retry > 0)
		{
			retry--;
			int x = cfg.rnd.nextInt(universeSize);
			int y = cfg.rnd.nextInt(universeSize);
			Point rndP = new Point(x, y);

			if (getSector(rndP) == null)
			{
				p = rndP;
			}
		}
		if (p == null)
			throw new Exception(".... ooppss it looks like the universe is FULL!!!\n" + toSquare());
		return p;
	}

	public Sheet getT()
	{
		return T;
	}

	public void setT(Sheet t)
	{
		T = t;
	}

	public Sheet getR()
	{
		return R;
	}

	public void setR(Sheet r)
	{
		R = r;
	}

	public Sheet getM()
	{
		return M;
	}

	public void setM(Sheet m)
	{
		M = m;
	}

	public Sheet getL()
	{
		return L;
	}

	public void setL(Sheet l)
	{
		L = l;
	}

	public int getUniverseSize()
	{
		return universeSize;
	}

	public Point getRandomPosition() throws Exception
	{
		Point position = null;
		position = new Point(universeSize, true);
		return position;
	}

	public void updateStardate(double t)
	{
		starDate = starDate - t;
	}

	public boolean contains(Point p)
	{
		if (p.getQuadrant() != 1)
			return false;

		if (p.getiX() > universeSize || p.getiY() > universeSize || p.getiZ() > universeSize)
			return false;

		return true;
	}

	public boolean thereAreEnemy(Point p)
	{
		return getEnemies(p) > 0;
	}

	private int getEnemies(Point p)
	{
		Sector s = getSector(p);
		if (s == null)
			return 0;
		return s.getEnemyAmount();
	}

	public Sector getSector(Point p)
	{
		return newUniverse[p.getiX()][p.getiY()];
	}

	public Sector getSector(int x, int y)
	{
		return newUniverse[x][y];
	}

}
